/**
 * MasicContainer
 * <p/>
 * User: joakimd
 * Date: 2013-03-11
 * Time: 11:28
 */
package org.dru.masic {
import flash.utils.getDefinitionByName;
import flash.utils.getQualifiedClassName;

import org.dru.masic.event.MasicContainerEvent;

public final class MasicContainer extends MasicObject {
    private var _speaker: MasicSpeaker;
    private var _components: Vector.<MasicComponent>;

    public function MasicContainer() {
        _speaker = new MasicSpeaker();
        _speaker.setParent(this);
        _components = new Vector.<MasicComponent>();
    }

    public function get speaker(): MasicSpeaker {
        return _speaker;
    }

    internal function get components(): Vector.<MasicComponent> {
        return _components;
    }

    public function get numComponents(): int {
        return components.length;
    }

    public function getComponentAt(index: int): MasicComponent {
        return components[index];
    }

    public function getComponentIndex(component: MasicComponent): int {
        return components.indexOf(component);
    }

    public function addComponent(component: MasicComponent): void {
        if (component.parent != null) {
            component.parent.removeComponent(component);
        }
        components.push(component);
        component.setParent(this);
        dispatchEvent(new MasicContainerEvent(MasicContainerEvent.COMPONENT_ADDED, component));
    }

    public function removeComponent(component: MasicComponent): void {
        const index: int = getComponentIndex(component);
        if (index != -1) {
            removeComponentAt(index);
        }
    }

    public function removeAllComponents(): void {
        for (var index: int = numComponents; --index >= 0;) {
            removeComponentAt(index);
        }
    }

    private function removeComponentAt(index: int): void {
        const component: MasicComponent = components.splice(index, 1)[0];
        component.removeAllConnections();
        component.setParent(null);
        dispatchEvent(new MasicContainerEvent(MasicContainerEvent.COMPONENT_REMOVED, component));
    }

    public function decode(containerData: Object): void {
        removeAllComponents();
        if (containerData.components != null) {
            const components: Array = containerData.components as Array;
            for each (var componentData: Object in components) {
                const className: String = componentData.className as String;
                const componentClass: Class = getDefinitionByName(className) as Class;
                const componentName: String = componentData.name as String;
                const component: MasicComponent = new componentClass() as MasicComponent;
                component.name = componentName;
                const parameters: Array = componentData.parameters as Array;
                for each (var parameterData: Object in parameters) {
                    const parameterName: String = parameterData.name as String;
                    const parameterSteps: int = parameterData.steps as int;
                    const parameterValue: Number = parameterData.value as Number;
                    for each (var parameter: MasicParameter in component.parameters) {
                        if (parameter.name == parameterName) {
                            parameter.steps = parameterSteps;
                            parameter.value = parameterValue;
                            break;
                        }
                    }
                    addComponent(component);
                }
            }
        }
        if (containerData.connections != null) {
            const connections: Array = containerData.connections as Array;
            for each (var connectionData: Object in connections) {
                const source: int = connectionData.source as int;
                const output: int = connectionData.output as int;
                const target: int = connectionData.target as int;
                const input: int = connectionData.input as int;
                getComponentAt(source).getOutputAt(output).addTarget(getComponentAt(target == -1 ? numComponents : target).getInputAt(input));
            }
        }
    }

    public function encode(): Object {
        const containerData: Object = {
            components: [],
            connections: []
        };
        for each (var component: MasicComponent in components) {
            // component
            const componentData: Object = {
                className: getQualifiedClassName(component),
                name: component.name,
                parameters: []
            };
            // parameters
            for each (var parameter: MasicParameter in component.parameters) {
                const parameterData: Object = {
                    name: parameter.name,
                    steps: parameter.steps,
                    value: parameter.value
                };
                componentData.parameters.push(parameterData);
            }
            containerData.components.push(componentData);
            // connections
            for each (var output: MasicOutput in component.outputs) {
                for each (var target: MasicInput in output.targets) {
                    const connectionData: Object = {};
                    connectionData.source = getComponentIndex(component);
                    connectionData.output = component.getOutputIndex(output);
                    connectionData.target = getComponentIndex(target.parent); // -1 for speaker
                    connectionData.input = target.parent.getInputIndex(target);
                    containerData.connections.push(connectionData);
                }
            }
        }
        return containerData;
    }
}
}
